#include "CompresorAritmeticoBase.h"
#include "PPMC.h"

CompresorAritmeticoBase::~CompresorAritmeticoBase()
{

}

void CompresorAritmeticoBase::comprimir(const Simbolo simbolo,const FrecuenciasSimbolos &frecuencias,
	const FrecuenciasSimbolos &excluir,std::ostream &salida)
{
	this->comprimirEquiprovable(simbolo,frecuencias,salida);
}

void CompresorAritmeticoBase::comprimirEquiprovable(const Simbolo simbolo,
	const FrecuenciasSimbolos &frecuencias,std::ostream &salida)
{
	salida.write((char *)&simbolo,sizeof(Simbolo));
}

Simbolo CompresorAritmeticoBase::descomprimir(const FrecuenciasSimbolos &frecuencias,
	const FrecuenciasSimbolos &excluir,std::istream &entrada)
{
	return this->descomprimirEquiprovable(frecuencias,entrada);
}

Simbolo CompresorAritmeticoBase::descomprimirEquiprovable(
	const FrecuenciasSimbolos &frecuencias,std::istream &entrada)
{
	Simbolo simbolo;
	entrada.read((char *)&simbolo,sizeof(Simbolo));
	return simbolo;
}


CompresorAritmeticoQueCalcula::CompresorAritmeticoQueCalcula()
{
	this->cantidadTotalDeBitsUsados=0;
}

CompresorAritmeticoQueCalcula::~CompresorAritmeticoQueCalcula()
{

}

/**
 * @pre Se llamo a PPMC::comprimir
 * Muestra en pantalla el tamaño del archivo comprimido.
 */
void CompresorAritmeticoQueCalcula::mostrarTamanyoArchivoComprimido()
{
	double size=this->cantidadTotalDeBitsUsados;

	cout << "Tamaño teorico del archivo comprimido: ";

	if (size<8)
	{
		cout << size << " bits" << endl;
		return;
	}

	size=size/8;

	if (size<1024)
	{
		cout << size << " bytes" << endl;
		return;
	}

	size=size/1024;

	if (size<1024)
	{
		cout << size << " KB" << endl;
		return;
	}

	size=size/1024;

	if (size<1024)
	{
		cout << size << " MB" << endl;
		return;
	}

	size=size/1024;

	cout << size << " GB" << endl;
}

void CompresorAritmeticoQueCalcula::addBitsACantidadTotal(const double probabilidad)
{
	this->cantidadTotalDeBitsUsados=this->cantidadTotalDeBitsUsados+
		-(log(probabilidad)/log(2));
}

void CompresorAritmeticoQueCalcula::comprimir(const Simbolo simbolo,
	const FrecuenciasSimbolos &frecuencias,
	const FrecuenciasSimbolos &excluir,std::ostream &salida)
{
	FrecuenciasSimbolos::const_iterator itFrecuencia=
		frecuencias.begin();
	double frecuenciaTotal=0;
	double frecuenciaSimbolo=0;

	// Recorremos las frecuencias a las que pertenece el simbolo a comprimir.
	while (itFrecuencia!=frecuencias.end())
	{
		FrecuenciasSimbolos::const_iterator itExcluir=
			excluir.find(itFrecuencia->first);

		double frecuencia=this->calcularFrecuenciaSimbolo(itFrecuencia->second);

		// En caso que no haya que excluir el simbolo iterado...
		if (itExcluir==excluir.end())
			frecuenciaTotal=frecuenciaTotal+frecuencia;

		// En caso de ser la frecuencia del simbolo la recuperamos
		if (itFrecuencia->first==simbolo)
			frecuenciaSimbolo=frecuencia;

		itFrecuencia++;
	}

	double probabilidad=frecuenciaSimbolo/frecuenciaTotal;
	this->addBitsACantidadTotal(probabilidad);
}

void CompresorAritmeticoQueCalcula::comprimirEquiprovable(const Simbolo simbolo,
	const FrecuenciasSimbolos &frecuencias,std::ostream &salida)
{
	this->addBitsACantidadTotal((double)1/(double)frecuencias.size());
}

double CompresorAritmeticoQueCalcula::calcularFrecuenciaSimbolo(
	const DatoSimboloModelo &datoSimboloModelo)
{
	return datoSimboloModelo.frecuencia;
}

double CompresorAritmeticoEnjecimientoHiperbolico::calcularFrecuenciaSimbolo(
	const DatoSimboloModelo &datoSimboloModelo)
{
	return ((double)datoSimboloModelo.frecuencia)/
		((double)(PPMC::getInstance()->getPosicionActualEnArchivo()-
		datoSimboloModelo.ultimaPosicionEnQueSeEncontro+1));
}
